#ifndef GAMEENGINE_GEOMETRY_TRGL2D_DEFS_H
#define GAMEENGINE_GEOMETRY_TRGL2D_DEFS_H

#include <GameEngine/Geometry/geometry_settings.h>

//=================================================================================

#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=================================================================================

namespace Trgl2D {
	//! Point index along a triangle
	enum Index {
		//! First point
		/*!
		 @note This is point at parametric coordinates (0,0)
		 */
		First = 0,
		//! Second point
		/*!
		 @note This is point at parametric coordinates (1,0)
		 */
		Second = 1,
		//! Third point
		/*!
		 @note This is point at parametric coordinates (0,1)
		 */
		Third = 2
	};
}

//=================================================================================
// Set of macros for the Trgl3d and Trgl3dFast classes

//! Compute the point along the triangle at given parametric coordinates
#define GAMEENGINE_IMPLEMENT_TRGL2D_PARAMETRIC_POINT(pt0, pt1, pt2, coords, type) \
	return Point2d<type>( \
				pt0.x() + coords.u()*(pt1.x()-pt0.x()) + coords.v()*(pt2.x()-pt0.x()), \
				pt0.y() + coords.u()*(pt1.y()-pt0.y()) + coords.v()*(pt2.y()-pt0.y()) \
			);

//=================================================================================

//! Compute the parametric coordinates along the triangle at given point
#define GAMEENGINE_IMPLEMENT_TRGL2D_PARAMETRIC_COORDS(pt, type) \
	Transformer2d<type> t = transformer(); \
	Point2d<type> tmp = t.transform(pt); \
	return Coords2d<type>(tmp.x(), tmp.y());

//=================================================================================

//! Compute a transformer that converts points into parametric coordinates along the triangle
#define GAMEENGINE_IMPLEMENT_TRGL2D_PARAMETRIC_TRANSFORMER(pt0, pt1, pt2, type) \
	return Transformer2d<type>(pt0,pt1,pt2,Point2d<type>(0,0),Point2d<type>(1,0), Point2d<type>(0,1));

//=================================================================================

//! Test wether a point is inside the triangle
#define GAMEENGINE_IMPLEMENT_TRGL2D_INSIDE(pt0, pt1, pt2, rhs, strict, type) \
	/* Get the sides of the triangle */ \
	type d1 = (pt1.x()-pt0.x())*(rhs.y()-pt0.y()) - (pt1.y()-pt0.y())*(rhs.x()-pt0.x()); \
	type d2 = (pt2.x()-pt1.x())*(rhs.y()-pt1.y()) - (pt2.y()-pt1.y())*(rhs.x()-pt1.x()); \
	type d3 = (pt0.x()-pt2.x())*(rhs.y()-pt2.y()) - (pt0.y()-pt2.y())*(rhs.x()-pt2.x()); \
	\
	/* Check the triangle belonging */ \
	if ( strict ) { \
		/* Check whether point lies on one of the (extended) triangle edges */ \
		/* If it does, point does not strictly belong to the triangle */ \
		if ( d1 == 0 ) return false; \
		if ( d2 == 0 ) return false; \
		if ( d3 == 0 ) return false; \
		\
		/* Check the sides */ \
		if ( ( d1 < 0 ) != ( d2 < 0 ) ) return false; \
		if ( ( d1 < 0 ) != ( d3 < 0 ) ) return false; \
	} \
	else { \
		/* Get the first non-null dot product (i.e., the first edge along which point does not lie) */ \
		type d = ( d1 == 0 ? ( d2 == 0 ? d3 : d2 ) : d1 ); \
		\
		/* Check the sides if and only if point is not on the edge */ \
		if ( ( d1 != 0 ) && ( ( d <= 0 ) != ( d1 <= 0 ) ) ) return false; \
		if ( ( d2 != 0 ) && ( ( d <= 0 ) != ( d2 <= 0 ) ) ) return false; \
		if ( ( d3 != 0 ) && ( ( d <= 0 ) != ( d3 <= 0 ) ) ) return false; \
	} \
	\
	/* If all sides are the same, point is in the triangle */ \
	return true;

//=================================================================================

//! Compute the intersection between the line and the triangle
#define GAMEENGINE_IMPLEMENT_TRGL2D_INTERSECTION_LINE(pt0, pt1, pt2, rhs, line_coordinate1, line_coordinate2, intersection_type, type) \
	int index = 0; \
	type tolerance = (type)0.000001; \
	\
	/* Test first point */ \
	{{ \
		type coord = rhs.closest_point(pt0); \
		Point2d<type> tmp = rhs.point(coord); \
		if ( tmp.get_square_distance(pt0) < tolerance ) {\
			++index; \
			line_coordinate1 = coord; \
			if ( intersection_type ) *intersection_type = IntersectionType::Intersection; \
		} \
	}} \
	\
	/* Test second point */ \
	{{ \
		type coord = rhs.closest_point(pt1); \
		Point2d<type> tmp = rhs.point(coord); \
		if ( tmp.get_square_distance(pt1) < tolerance ) {\
			++index; \
			if ( index == 0 ) { \
				line_coordinate1 = coord; \
				if ( intersection_type ) *intersection_type = IntersectionType::Intersection; \
			} \
			else { \
				line_coordinate2 = coord; \
				if ( intersection_type ) *intersection_type = IntersectionType::Parallel; \
				return true; \
			} \
		} \
	}} \
	\
	/* Test third point */ \
	{{ \
		type coord = rhs.closest_point(pt2); \
		Point2d<type> tmp = rhs.point(coord); \
		if ( tmp.get_square_distance(pt2) < tolerance ) {\
			++index; \
			if ( index == 0 ) { \
				line_coordinate1 = coord; \
				if ( intersection_type ) *intersection_type = IntersectionType::Intersection; \
			} \
			else { \
				line_coordinate2 = coord; \
				if ( intersection_type ) *intersection_type = IntersectionType::Parallel; \
				return true; \
			} \
		} \
	}} \
	\
	/* Test first line */ \
	{{ \
		Line2d<type> line(pt0,pt1); \
		type coord, coord2; \
		IntersectionType::Type tmp = line.intersection(rhs, coord, &coord2); \
		if ( tmp == IntersectionType::Intersection ) { \
			if ( ( coord >= 0 ) && ( coord <= 1 ) ) { \
				++index; \
				if ( index == 0 ) { \
					line_coordinate1 = coord2; \
					if ( intersection_type ) *intersection_type = IntersectionType::Intersection; \
				} \
				else { \
					line_coordinate2 = coord2; \
					return true; \
				} \
			} \
		} \
	}} \
	\
	/* Test second line */ \
	{{ \
		Line2d<type> line(pt1,pt2); \
		type coord, coord2; \
		IntersectionType::Type tmp = line.intersection(rhs, coord, &coord2); \
		if ( tmp == IntersectionType::Intersection ) { \
			if ( ( coord >= 0 ) && ( coord <= 1 ) ) { \
				++index; \
				if ( index == 0 ) { \
					line_coordinate1 = coord2; \
					if ( intersection_type ) *intersection_type = IntersectionType::Intersection; \
				} \
				else { \
					line_coordinate2 = coord2; \
					return true; \
				} \
			} \
		} \
	}} \
	\
	/* Test third line */ \
	{{ \
		Line2d<type> line(pt2,pt0); \
		type coord, coord2; \
		IntersectionType::Type tmp = line.intersection(rhs, coord, &coord2); \
		if ( tmp == IntersectionType::Intersection ) { \
			if ( ( coord >= 0 ) && ( coord <= 1 ) ) { \
				++index; \
				if ( index == 0 ) { \
					line_coordinate1 = coord2; \
					if ( intersection_type ) *intersection_type = IntersectionType::Intersection; \
					return true; \
				} \
				else { \
					line_coordinate2 = coord2; \
					return true; \
				} \
			} \
		} \
	}} \
	\
	/* Line does not intersect with triangle */ \
	if ( intersection_type ) *intersection_type = IntersectionType::NoIntersection; \
	return false;

//=================================================================================

//! Compute the parametric coordinates of the closest point along the triangle
#define GAMEENGINE_IMPLEMENT_TRGL2D_CLOSEST_POINT(pt0, pt1, pt2, rhs, coords, type) \
	/* Test the if point is already inside the triangle */ \
	if ( inside(rhs, false) == true ) { \
		Transformer2d<type> transformer(pt0, pt1, pt2, Point2d<type>((type)0,(type)0), \
			Point2d<type>((type)1,(type)0), Point2d<type>((type)0,(type)1)); \
		Point2d<type> tmp = transformer.transform(rhs); \
		coords.u() = tmp.x(); \
		coords.v() = tmp.y(); \
		return true; \
	} \
	\
	/* Find the two closest points */ \
	int index1 = 0, index2 = 1; \
	if ( rhs.get_square_distance(pt0) > rhs.get_square_distance(pt1) ) { \
		index1 = 1; \
		index2 = 0; \
	} \
	if ( rhs.get_square_distance(operator[]((Trgl2D::Index)index2)) > rhs.get_square_distance(pt2) ) { \
		index2 = 2; \
	} \
	\
	/* Test if point falls out of the line */ \
	Line2d<type> closest_line(operator[]((Trgl2D::Index)index1), operator[]((Trgl2D::Index)index2)); \
	type param = closest_line.closest_point(rhs); \
	if ( param <= 0 ) { \
		if ( index1 == 0 ) { \
			coords = Coords2d<type>(0,0); \
			return true; \
		} \
		if ( index1 == 1 ) { \
			coords = Coords2d<type>(1,0); \
			return true; \
		} \
		coords = Coords2d<type>(0,1); \
		return true; \
	} \
	if ( param >= 1 ) { \
		if ( index2 == 0 ) { \
			coords = Coords2d<type>(0,0); \
			return true; \
		} \
		if ( index2 == 1 ) { \
			coords = Coords2d<type>(1,0); \
			return true; \
		} \
		coords = Coords2d<type>(0,1); \
		return true; \
	} \
	\
	/* Test if point falls in the line */ \
	Coords2d<type> res(0,0); \
	if ( index1 == 1 ) res.u() += ( (type)1.0 - param ); \
	else if ( index1 == 2 ) res.v() += ( (type)1.0 - param ); \
	if ( index2 == 1 ) res.u() += param; \
	else if ( index2 == 2 ) res.v() += param; \
	\
	/* Return closest point */ \
	coords = res; \
	return true;

//=================================================================================

//! Compute the perimeter of the triangle
#define GAMEENGINE_IMPLEMENT_TRGL2D_PERIMETER(pt0, pt1, pt2, type) \
	return (type)(pt0.get_distance(pt1) + pt1.get_distance(pt2) + pt2.get_distance(pt0));

//=================================================================================

//! Compute the area of the triangle
#define GAMEENGINE_IMPLEMENT_TRGL2D_AREA(pt0, pt1, pt2, type) \
	return ( (pt1.x()-pt0.x())*(pt2.y()-pt0.y()) - (pt1.y()-pt0.y())*(pt2.x()-pt0.x()) ) / (type)2.0; \

//=================================================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
